﻿using System;
using System.Collections.Generic;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using YJKRevitDimensionText.Utility;

namespace YJKRevitDimensionText.Commands
{
	[Transaction(TransactionMode.Manual)]
	public class CmdBreakLineSegDimension : IExternalCommand
	{
		public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
		{
			try
			{
				UIDocument activeUIDocument = commandData.Application.ActiveUIDocument;
				Document document = activeUIDocument.Document;
				for (;;)
				{
					Reference reference = activeUIDocument.Selection.PickObject((Autodesk.Revit.UI.Selection.ObjectType)1, new CmdBreakLineSegDimension.DimensionSelectionFilter(), "请在多段直线型标注对象上拾取需要被打断的位置点<ESC>:");
					Dimension dim = document.GetElement(reference) as Dimension;
					XYZ globalPoint = reference.GlobalPoint;
					int idxSeg = -1;
					Reference breakRefByPoint = this.GetBreakRefByPoint(dim, globalPoint, ref idxSeg);
					this.BreakDimByReference(dim, breakRefByPoint, idxSeg);
				}
			}
			catch
			{
			}
			return Autodesk.Revit.UI.Result.Succeeded;
		}

		private List<CmdBreakLineSegDimension.DimensionSegmentInfo> GetDimSegByPoint(Dimension dim, XYZ ptTest, ref int index)
		{
			List<CmdBreakLineSegDimension.DimensionSegmentInfo> list = new List<CmdBreakLineSegDimension.DimensionSegmentInfo>();
            XYZ origin = dim.Segments.get_Item(0).Origin;
            XYZ xyz = (dim.Segments.get_Item(1).Origin - origin).Normalize();
			int size = dim.Segments.Size;
			for (int i = 0; i < size; i++)
			{
                DimensionSegment dimensionSegment = dim.Segments.get_Item(i);
				if (dimensionSegment.Value == null)
				{
					throw new Exception("未知情况!");
				}
				CmdBreakLineSegDimension.DimensionSegmentInfo dimensionSegmentInfo = new CmdBreakLineSegDimension.DimensionSegmentInfo();
				dimensionSegmentInfo.DimSeg = dimensionSegment;
				dimensionSegmentInfo.DimSegIndex = i;
				double num = dimensionSegment.Value.Value * 0.5;
				dimensionSegmentInfo.PointStart = dimensionSegmentInfo.PointMid - xyz * num;
				dimensionSegmentInfo.PointEnd = dimensionSegmentInfo.PointMid + xyz * num;
                dimensionSegmentInfo.StartReference = dim.References.get_Item(i);
                dimensionSegmentInfo.EndReference = dim.References.get_Item(i+1);
				list.Add(dimensionSegmentInfo);
				XYZ xyz2 = dimensionSegmentInfo.PointStart - ptTest;
				XYZ xyz3 = dimensionSegmentInfo.PointEnd - ptTest;
				if (xyz2.DotProduct(xyz3) <= 1E-06)
				{
					index = i;
				}
			}
			return list;
		}

		private Reference GetBreakRefByPoint(Dimension dim, XYZ ptTest, ref int idxDimSeg)
		{
			int num = -1;
			List<CmdBreakLineSegDimension.DimensionSegmentInfo> dimSegByPoint = this.GetDimSegByPoint(dim, ptTest, ref num);
			Reference result;
			if (num == 0)
			{
				result = dimSegByPoint[num].EndReference;
			}
			else if (num == dim.Segments.Size - 1)
			{
				result = dimSegByPoint[num].StartReference;
			}
			else
			{
				result = dimSegByPoint[num].GetNearReferenceByPoint(ptTest);
			}
			idxDimSeg = num;
			return result;
		}

		private Element GetElementByReference(Autodesk.Revit.DB.Document doc, Reference refElem)
		{
			if (refElem.LinkedElementId != ElementId.InvalidElementId)
			{
				return (doc.GetElement(refElem.ElementId) as RevitLinkInstance).GetLinkDocument().GetElement(refElem.LinkedElementId);
			}
			return doc.GetElement(refElem.ElementId);
		}

		private Reference GetReferenceByDoc(Autodesk.Revit.DB.Document doc, Reference refElem)
		{
			if (refElem.LinkedElementId != ElementId.InvalidElementId)
			{
				return refElem.CreateReferenceInLink();
			}
			return refElem;
		}

		private List<Dimension> BreakDimByReference(Dimension dim, Reference refBreak, int idxSeg)
		{
			List<Dimension> list = new List<Dimension>();
			Document document = dim.Document;
			string b = refBreak.ConvertToStableRepresentation(document);
			ReferenceArray referenceArray = new ReferenceArray();
			ReferenceArray referenceArray2 = new ReferenceArray();
			bool flag = false;
			foreach (object obj in dim.References)
			{
				Reference reference = (Reference)obj;
				Reference reference2 = DimUtility.ConvertDimReference(document, reference);
				if (!flag)
				{
					referenceArray.Append(reference2);
					if (reference.ConvertToStableRepresentation(document) == b)
					{
						flag = true;
						referenceArray2.Append(reference2);
					}
				}
				else
				{
					referenceArray2.Append(reference2);
				}
			}
            Line line = Line.CreateBound(dim.Segments.get_Item(0).Origin, dim.Segments.get_Item(1).Origin);
			Transaction transaction = new Transaction(document, "CreateDim");
			try
			{
				transaction.Start();
				Dimension item = document.Create.NewDimension(dim.Document.ActiveView, line, referenceArray, dim.DimensionType);
				Dimension item2 = document.Create.NewDimension(dim.Document.ActiveView, line, referenceArray2, dim.DimensionType);
				list.Add(item);
				list.Add(item2);
				document.Delete(dim.Id);
				transaction.Commit();
			}
			catch
			{
				transaction.RollBack();
			}
			return list;
		}

		private class DimensionSelectionFilter : ISelectionFilter
		{
			public bool AllowElement(Element elem)
			{
				Dimension dimension = elem as Dimension;
				return dimension != null && dimension.DimensionShape == null && dimension.Segments.Size > 0;
			}

			public bool AllowReference(Reference reference, XYZ position)
			{
				throw new NotImplementedException();
			}
		}

		private class DimensionSegmentInfo
		{
			public Reference GetNearReferenceByPoint(XYZ ptTest)
			{
				double num = ptTest.DistanceTo(this.PointEnd);
				double num2 = ptTest.DistanceTo(this.PointStart);
				if (num - num2 < 1E-06)
				{
					return this.EndReference;
				}
				return this.StartReference;
			}

			public double DistParam { get; set; }

			public DimensionSegment DimSeg { get; set; }

			public int DimSegIndex { get; set; }

			public XYZ PointStart { get; set; }

			public XYZ PointEnd { get; set; }

			public XYZ PointMid
			{
				get
				{
					return this.DimSeg.Origin;
				}
			}

			public Reference StartReference { get; set; }

			public Reference EndReference { get; set; }
		}

		private class DimensionSegmentInfoComparer : IComparer<CmdBreakLineSegDimension.DimensionSegmentInfo>
		{
			public int Compare(CmdBreakLineSegDimension.DimensionSegmentInfo x, CmdBreakLineSegDimension.DimensionSegmentInfo y)
			{
				if (Math.Abs(x.DistParam - y.DistParam) <= CmdBreakLineSegDimension.DimensionSegmentInfoComparer.dEpsilon)
				{
					return 0;
				}
				if (y.DistParam - x.DistParam > CmdBreakLineSegDimension.DimensionSegmentInfoComparer.dEpsilon)
				{
					return -1;
				}
				return 1;
			}

			public static double dEpsilon = 1E-06;
		}
	}
}
